LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

SpringMVC[Mapping,中文乱码,Restful及跨域,JSON序列化,非简单请求,拦截器]

2024/1/15 后端

Spring MVC

内容 说明 重要程度
Spring MVC入门 Spring MVC开发流程与环境配置 ★★★★★★
接收Web数据 Spring MVC参数接收与数据绑定 ★★★★★★
URL Mapping 讲解URL绑定过程 ★★★★★★
中文乱码问题 解决请求与相应中文乱码 ★★★★★★
拦截器 Spring MVC拦截器的使用 ★★★

Restful开发风格 [主流]

内容 说明 重要程度
Restful风格介绍 介绍Restful开发规范 ★★★★★★
Restful开发实战 实例讲解Restful在Spring MVC中的实现 ★★★★★★
JSON序列化 通过相应输出数据 ★★★★★★
Restful的跨域问题 分析跨域问题的来源与解决办法 ★★★★★★

Spring MVC[Model+View+Controller(中介)]

比servlet方便许多 简化web程序的开发

  • Spring MVC是Spring体系的轻量级Web MVC框架
  • Spring MVC的核心Controller控制器,用于处理请求,产生相应
  • Spring MVC基于Spring IOC容器运行,所有对象被IoC管理
学习向导
  • Spring MVC入门
  • Spring MVC数据绑定
  • Restful 开发风格
  • Spring MVC拦截器

Spring MVC环境配置

IDEA环境下创建Maven WebApp

Project Structure 点加号 添加Web 右侧Path是Web描述符所存储的路径
C:\Users\Pluminary\Desktop\SpringMVC\src\main\webapp\WEB-INF\web.xml
Deployment descriptor version => 3.1
下面的 Web Resource Directory【用于目录存储页面】
C:\Users\Pluminary\Desktop\SpringMVC\src\main\webapp
再次点下方的Create Artifact
右上角Type:
Web Application: Exploded 目录方式运行
Web Application: Archive 打包成war包运行
配置Tomcat Server => Deployment 添加当前工程 SpringMVC:Web exploded
下面的Application context: 设置为 / 只访问localhost即可
Server选项框里的 On ‘Update’ action: 把Restart server 改成 Update classes and resources【热部署】
当页面源代码发生变化时 不需要重启

  • Maven依赖Spring-WebMVC
  • web.xml配置DispatcherServlet
  • 配置applicationContext的mvc标记
  • 开发Controller控制器
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.imooc</groupId>
    <artifactId>first-springmvc</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>
    </dependencies>
</project>
src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
<!--    DispatchServlet 对所有请求进行拦截  -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
<!--        DispatcherServlet是Spring MVC是最核心的对象
            DispatcherServlet用于拦截http请求
            并根据请求的URL调用与之对应的Controller方法,来完成Http请求的处理
-->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--        在Web应用启动时自动创建Spring IoC容器,并初始化DispatcherServlet -->
<!--        applicationContext.xml 初始化参数 启动时候加载-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
<!--        "/" 所有请求都要拦截-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mv="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- context:component-scan标签作用 在Spring IoC初始化过程中, 自动创建并管理com.imooc.springmvc
   及子包中拥有以下注解的对象:
       @Repository 通常存放在Dao类上 通常都是与数据发生直接交互的类
       @Service     ...Service类上 业务逻辑类
       @Controller 描述SpringMVC的控制器类
       @Component  无法确定的类型种类
   -->
    <context:component-scan base-package="com.imooc.springmvc"></context:component-scan>
<!--    启用Spring MVC的注解开发模式-->
    <mvc:annotation-driven/>
<!--    将图片/JS/CSS等静态资源排除在外, 可提高执行效率-->
    <mvc:default-servlet-handler/>
</beans>
com/imooc/springmvc/controller/TestController.java
package com.imooc.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
//  将当前的方法绑定某个get方式请求的url  //localhost/t
    @GetMapping("/t")
//  直接向响应输出字符串数据,不跳转页面
    @ResponseBody
    public String test(){
        return "SUCCESS";
    }
}

添加依赖jar包进入项目工程 配置tomcat服务Run/Debug Configurations 中的 Deployment 点一下小铅笔修改 将右侧的Avaliable Elements中的依赖包全部put进入
404bug 配置都正常无误 寻找一下项目工程里的out/artifacts/WEB-INF/web.xml

Spring MVC数据绑定

URL Mapping(URL映射)
  • URL Mapping指将URL与Controller方法绑定
  • 通过将URL与方法绑定,SpringMVC便可通过Tomcat对外暴露服务
URL Mapping注解
  • @RequestMapping - 通用绑定 //在全局用则是通用请求映射访问前缀 若在方法上则不区分get/post请求
  • @GetMapping - 绑定Get请求
  • @PostMapping - 绑定Post请求

@GetMapping("/g") ====== @RequestMapping(value="/g",method=RequestMethod.GET)

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.imooc</groupId>
    <artifactId>first-springmvc</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.28</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>
</project>
src/main/webapp/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--    DispatchServlet 对所有请求进行拦截  -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <!--        DispatcherServlet是Spring MVC是最核心的对象
                    DispatcherServlet用于拦截http请求
                    并根据请求的URL调用与之对应的Controller方法,来完成Http请求的处理
        -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--        在Web应用启动时自动创建Spring IoC容器,并初始化DispatcherServlet -->
        <!--        applicationContext.xml 初始化参数 启动时候加载-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--        "/" 所有请求都要拦截-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mv="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- context:component-scan标签作用 在Spring IoC初始化过程中, 自动创建并管理com.imooc.springmvc
       及子包中拥有以下注解的对象:
           @Repository 通常存放在Dao类上 通常都是与数据发生直接交互的类
           @Service     ...Service类上 业务逻辑类
           @Controller 描述SpringMVC的控制器类
           @Component  无法确定的类型种类
       -->
    <context:component-scan base-package="com.imooc.springmvc"></context:component-scan>
    <!--    启用Spring MVC的注解开发模式-->
    <mvc:annotation-driven/>
    <!--    将图片/JS/CSS等静态资源排除在外, 可提高执行效率-->
    <mvc:default-servlet-handler/>
</beans>
com/imooc/springmvc/controller/TestController.java
package com.imooc.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class TestController {
    @GetMapping("/t") //localhost/t
    @ResponseBody //直接向响应输出字符串数据,不跳转页面
    public String test(){
        return "Hello Spring MVC";
    }
}
com/imooc/springmvc/controller/URLMappingController.java
package com.imooc.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping ("/um") //类上直接使用默认访问前缀 全局通用请求映射
public class URLMappingController {
    @GetMapping("/g")
    @ResponseBody
    public String getMapping(){
        return "This is get method";
    }
//  直接访问会出错 post请求如何访问 用进行html表单提交
    @PostMapping("/p")
    @ResponseBody
    public String postMapping(){
        return "This is post method";
    }
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/um/p" method="post">
        <input type="submit" value="提交">
    </form>
</body>
</html>

Controller方法参数接收请求参数

接收请求参数的常用做法
  • 使用Controller方法参数接收
<form action="/m1" method="post">
    <input name="username"/>
    <input name="password"/>
</form>
----------------------------------------------------
@PostMapping("/m1")
@ResponseBody
public String post(String username, Long password){
    return username + ":" + password;
}

特殊的注解@RequestParam("") 接收特殊自定义的参数

com/imooc/springmvc/controller/URLMappingController.java
package com.imooc.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/um") //类上直接使用默认访问前缀 全局通用请求映射
public class URLMappingController {
    @GetMapping("/g")
    @ResponseBody
    public String getMapping(@RequestParam("manager_name") String managerName) {
        System.out.println("managerName:"+managerName);
        return "This is get method";
    }

    //    直接访问会出错 post请求如何访问 用进行html表单提交
    @PostMapping("/p")
    @ResponseBody
    public String postMapping(String username, String password) {
        System.out.println(username + ":" + password);
        return "This is post method";
    }
}

=========================
网页中输入 http://localhost/um/g?manager_name=lily
控制台返回 managerName:lily
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/um/p" method="post">
        <input name="username"><br/>
        <input name="password"><br/>
        <input type="submit" value="提交">
    </form>
</body>
</html>
  • 使用Java Bean接收数据
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/um/p1" method="post">
        <input name="username"><br/>
        <input name="password"><br/>
        <input type="submit" value="提交">
    </form>
</body>
</html>

​ 用实体对象User进行接收多个对象的创建和注入以及类型转换 [一次性完成]

com/imooc/springmvc/controller/URLMappingController.java
@PostMapping("/p1")
    @ResponseBody //只要拥有User属性和参数 就可以一起赋值
    public String postMapping1(User user, String username){
        System.out.println(user.getUsername() + ":" + user.getPassword());
        return "This is post method";
    }
User.java
//标准的java bean
public class User {
    private String username;
    private Long password;
} Setter + Getter

踩坑记录:IDEA web项目out artifacts文件夹只包含WEB-INF_out artifacts无法生成test.html-CSDN博客

综合训练:学员调查问卷

知识点
  • 利用数组或者List接收请求中的复合数据
  • 利用@RequestParam为参数设置默认值
  • 使用Map对象接收请求参数及注意事项
URI绝对路径与相对路径

相对地址的应用案例
src/main/webapp/form.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学员调查问卷</title>
    <style>
        .container {
            position: absolute;
            border: 1px solid #cccccc;
            left: 50%;
            top: 50%;
            width: 400px;
            height: 300px;
            margin-left: -200px;
            margin-top: -150px;
            box-sizing: border-box;
            padding: 10px;
        }
        h2{
            margin: 10px 0px;
            text-align: center;
        }
        h3{
            margin: 10px  0px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>学员调查问卷</h2>
        <form action="./apply" method="post">
        <h3>您的姓名</h3>
        <input name="name" class="text"  style="width: 150px">
        <h3>您正在学习的技术方向</h3>
        <select name="course" style="width: 150px">
            <option value="java">Java</option>
            <option value="h5">HTML5</option>
            <option value="python">Python</option>
            <option value="php">PHP</option>
        </select>
        <div>
<!--复选框用数组 或 ArrayList接收-->
            <h3>您的学习目的:</h3>
            <input type="checkbox" name="purpose" value="1">就业找工作
            <input type="checkbox" name="purpose" value="2">工作要求
            <input type="checkbox" name="purpose" value="3">兴趣爱好
            <input type="checkbox" name="purpose" value="4">其他
        </div>
        <div style="text-align: center;padding-top:10px" >
            <input type="submit" value="提交" style="width:100px">
        </div>
        </form>

    </div>
</body>
</html>

如果要接收复合数据 直接用数组接收是最简单的 但使用起来并不轻松
可以使用List来接收 但是前方记得要有@RequestParam
更加高级的可以封装成一个对象 用List接收复合数据
Map只能接收单个数据 接收复合数据的时候会造成数据丢失

com/imooc/springmvc/controller/FormController.java
package com.imooc.springmvc.controller;

import com.imooc.springmvc.entity.Form;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;

@Controller
public class FormController {
//    @PostMapping("/apply")  ★★★第一次尝试★★★
    @ResponseBody
//    如果在请求中不包含默认值 => 取别名  默认值机制:ANON匿名
    public String apply(@RequestParam(value = "n", defaultValue = "ANON") String name, String course, Integer[] purpose){
        System.out.println(name);
        System.out.println(course);
        for (Integer p:purpose){
            System.out.println(p);
        }
        return "SUCCESS";
    }

//    @PostMapping("/apply")  ★★★第二次尝试★★★
    // 请求中的复合数据要被转换为List进行存储
    @ResponseBody
    public String apply(String name, String course, @RequestParam List<Integer> purpose){
        System.out.println(name);
        System.out.println(course);
        for (Integer p:purpose){
            System.out.println(p);
        }
        return "SUCCESS";
    }

    @PostMapping("/apply")  
    //★★★★★ 推荐使用 ★★★★★
    // 实体类+List 极大简化表单工作量  
    // 请求中的复合数据要被转换为List进行存储
    @ResponseBody
    public String apply(Form form){
        return "SUCCESS";
    }
    //不推荐直接使用Map导入数据 因为复合数据[数组数据]会丢失
}
com/imooc/springmvc/entity/Form.java
public class Form {
    private String name;
    private String course;
    private List<Integer> purpose;
}Setter + Getter

关联对象赋值

复杂内容表单
用户名: <input name="username">
密码:<input name="password">
--------------------------------
姓名:<input name="name">
身份证号:<input name="idno">
过期时间:<input name="expire">
面向对象设计 【关联对象赋值】
public class User{
    private String username;
    private String password;
    private IDcard idcard = new IDCard();
    //Getter + Setter
}
★★ 关联上IDcard ★★
public class IDcard{
    private String name;
    private String idno;
    private Date expire;
    //Getter + Setter
}
用户名: <input name="username">
密码:<input name="password">
--------------------------------
姓名:<input name="idcard.name">
身份证号:<input name="idcard.idno">
过期时间:<input name="idcard.expire">
src/main/webapp/form.html
 <div>
            <h3>您的学习目的:</h3>
            <input type="checkbox" name="purpose" value="1">就业找工作
            <input type="checkbox" name="purpose" value="2">工作要求
            <input type="checkbox" name="purpose" value="3">兴趣爱好
            <input type="checkbox" name="purpose" value="4">其他
        </div>
            <h3>收货人</h3>
<!--  private Delivery delivery = new Delivery() -->
            <input name="delivery.name" class="text" style="width: 150px">
            <h3>联系电话</h3>
            <input name="delivery.mobile" class="text" style="width: 150px">
            <h3>收货地址</h3>
            <input name="delivery.address" class="text" style="width: 150px">

        <div style="text-align: center;padding-top:10px" >
            <input type="submit" value="提交" style="width:100px">
        </div>
com/imooc/springmvc/entity/Form.java
public class Form {
    private String name;
    private String course;
    private List<Integer> purpose;
    private Delivery delivery = new Delivery();
} Getter + Setter
com/imooc/springmvc/entity/Delivery.java
public class Delivery {
    private String name;
    private String address;
    private String mobile;
} Getter + Setter
com/imooc/springmvc/controller/FormController.java
package com.imooc.springmvc.controller;

import com.imooc.springmvc.entity.Form;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;

@Controller
public class FormController {
//    @PostMapping("/apply")  第一次尝试
    @ResponseBody
//    如果在请求中不包含默认值 => 取别名  默认值机制:ANON匿名
    public String apply(@RequestParam(value = "n", defaultValue = "ANON") String name, String course, Integer[] purpose){
        System.out.println(name);
        System.out.println(course);
        for (Integer p:purpose){
            System.out.println(p);
        }
        return "SUCCESS";
    }

//    @PostMapping("/apply")  第二次尝试
    // 请求中的复合数据要被转换为List进行存储
    @ResponseBody
    public String apply(String name, String course, @RequestParam List<Integer> purpose){
        System.out.println(name);
        System.out.println(course);
        for (Integer p:purpose){
            System.out.println(p);
        }
        return "SUCCESS";
    }

//    @PostMapping("/apply") 第三次尝试[这个可以 但有更好]
    // 实体类+List 极大简化表单工作量
    // 请求中的复合数据要被转换为List进行存储
    @ResponseBody
    public String apply(Form form){
        return "SUCCESS";
    }

    @PostMapping("/apply")
    @ResponseBody
    public String applyDelivery(Form form){
        System.out.println(form.getDelivery().getName());
        return "SUCCESS";
    }
}

日期类型转换

①注解方法

com/imooc/springmvc/controller/URLMappingController.java
@Controller
@RequestMapping("/um") //类上直接使用默认访问前缀 全局通用请求映射
public class URLMappingController {
@PostMapping("/p1")
    @ResponseBody
    public String postMapping1(User user, String username, @DateTimeFormat(pattern = "yyyy-MM-dd") Date createTime){
        System.out.println(user.getUsername() + ":" + user.getPassword());
        return "This is post method";
    }
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/um/p1" method="post">
        <input name="username"><br/>
        <input name="password"><br/>
        <input name="createTime"><br/>
        <input type="submit" value="提交">
    </form>
</body>
</html>


②实体对象接收数据

com/imooc/springmvc/entity/User.java
//标准的java bean
public class User {
    private String username;
    private Long password;
// SpringMVC会自动按照这个类型进行转换
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date createTime;
} Getter + Setter

③自定义转换器:全局的默认时间转换器

com/imooc/springmvc/converter/MyDateConverter.java
package com.imooc.springmvc.converter;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyDateConverter implements Converter<String, Date> {
    public Date convert(String s) { //转换工作
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date d = sdf.parse(s);
            return d;
        } catch (ParseException e) {
            return null;
        }
    }
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mv="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- context:component-scan标签作用 在Spring IoC初始化过程中, 自动创建并管理com.imooc.springmvc
       及子包中拥有以下注解的对象:
           @Repository 通常存放在Dao类上 通常都是与数据发生直接交互的类
           @Service     ...Service类上 业务逻辑类
           @Controller 描述SpringMVC的控制器类
           @Component  无法确定的类型种类
       -->
    <context:component-scan base-package="com.imooc.springmvc"></context:component-scan>
    <!--    启用Spring MVC的注解开发模式 让底下的转换类生效-->
    <mvc:annotation-driven conversion-service="conversionService"/>
    <!--    将图片/JS/CSS等静态资源排除在外, 可提高执行效率-->
    <mvc:default-servlet-handler/>
    <!--    通知SpringMVC有哪些转换类-->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!--    这是转换类定义的地方-->
        <property name="converters">
            <set>
                <bean class="com.imooc.springmvc.converter.MyDateConverter"/>
            </set>
        </property>
    </bean>
</beans>

解决中文乱码问题

Web应用的中文乱码由来
  • Tomcat默认使用字符集ISO-8859-1,属于西欧字符集
  • 解决乱码的核心思路是将ISO-8859-1转换为UTF-8
  • Controller中请求与响应都需要设置UTF-8字符集
中文乱码的配置
  • Get请求乱码 - server.xml增加URIEncoding属性

去Tomcat-conf文件中寻找server.xml进行增加

 <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               URIEncoding="UTF-8"
               maxParameterCount="1000"
               />

===============================================
http://localhost/um/g?manager_name=丽丽
控制台:managerName:丽丽
  • Post请求乱码 - web.xml配置CharacterEncodingFilter
http://localhost/
输入:
张三  123456 2001-03-02
随后点击提交
控制台:??????:123456
src/main/webapp/WEB-INF/web.xml
<filter>
        <filter-name>characterFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
http://localhost/
输入:
张三  123456 2001-03-02
随后点击提交
控制台:张三:123456
  • Response相应乱码 - Spring配置StringHttpMessageConverter

解决响应中的中文乱码

com/imooc/springmvc/controller/URLMappingController.java
package com.imooc.springmvc.controller;

import com.imooc.springmvc.entity.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

@Controller
@RequestMapping("/um") //类上直接使用默认访问前缀 全局通用请求映射
public class URLMappingController {
    @GetMapping("/g")
    @ResponseBody
    public String getMapping(@RequestParam("manager_name") String managerName) {
        System.out.println("managerName:"+managerName);
        return "This is get method";
    }

    //    直接访问会出错 post请求如何访问 用进行html表单提交
    @PostMapping("/p")
    @ResponseBody
    public String postMapping(String username, Long password) {
        System.out.println(username + ":" + password);
        return "This is post method";
    }

//    @PostMapping("/p1")
    @ResponseBody
    public String postMapping1(User user){
        System.out.println(user.getUsername() + ":" + user.getPassword());
        return "This is post method";
    }

    @PostMapping("/p1")
    @ResponseBody
    public String postMapping1(User user, String username, @DateTimeFormat(pattern = "yyyy-MM-dd") Date createTime){
        System.out.println(user.getUsername() + ":" + user.getPassword());
        return "这是Post响应";
    }
}
User.java
//标准的java bean
public class User {
    private String username;
    private Long password;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date createTime;
}Getter + Setter
com/imooc/springmvc/converter/MyDateConverter.java
package com.imooc.springmvc.converter;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MyDateConverter implements Converter<String, Date> {
    public Date convert(String s) { //转换工作
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date d = sdf.parse(s);
            return d;
        } catch (ParseException e) {
            return null;
        }
    }
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/um/p1" method="post">
        <input name="username"><br/>
        <input name="password"><br/>
        <input name="createTime"><br/>
        <input type="submit" value="提交">
    </form>
</body>
</html>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mv="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- context:component-scan标签作用 在Spring IoC初始化过程中, 自动创建并管理com.imooc.springmvc
       及子包中拥有以下注解的对象:
           @Repository 通常存放在Dao类上 通常都是与数据发生直接交互的类
           @Service    ...Service类上 业务逻辑类
           @Controller 描述SpringMVC的控制器类
           @Component  无法确定的类型种类
       -->
    <context:component-scan base-package="com.imooc.springmvc"></context:component-scan>
    <!--    启用Spring MVC的注解开发模式 让底下的转换类生效-->
    <mvc:annotation-driven conversion-service="conversionService">
<!--        设置消息转换器-->

        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
<!--                转换什么呢?-->
                <property name="supportedMediaTypes">
                    <list>
<!--  在servlet中是直接  response.setContentType("text/html;charset=utf-8")  -->
                        <value>text/html;charset=utf-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>

    </mvc:annotation-driven>
    <!--    将图片/JS/CSS等静态资源排除在外, 可提高执行效率-->
    <mvc:default-servlet-handler/>
    <!--    通知SpringMVC有哪些转换类-->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!--    这是转换类定义的地方-->
        <property name="converters">
            <set>
                <bean class="com.imooc.springmvc.converter.MyDateConverter"/>
            </set>
        </property>
    </bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--    DispatchServlet 对所有请求进行拦截  -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <!--        DispatcherServlet是Spring MVC是最核心的对象
                    DispatcherServlet用于拦截http请求
                    并根据请求的URL调用与之对应的Controller方法,来完成Http请求的处理
        -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--        在Web应用启动时自动创建Spring IoC容器,并初始化DispatcherServlet -->
        <!--        applicationContext.xml 初始化参数 启动时候加载-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--        "/" 所有请求都要拦截-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <filter>
        <filter-name>characterFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

响应中产生结果 [ModelAndView => MVC高效解耦理念]

  • @ResponseBody - 产生响应文本

    • @ResponseBody直接产生响应体数据, 过程不涉及任何视图
    • @ResponseBody可产生标准字符串/JSON/XML等格式数据
    • @ResponseBody被StringHttpMessageConverter所影响
  • ModelAndView - 利用模板引擎渲染输出

    • ModelAndView对象是指”模型(数据)与视图(界面)”对象
    • 通过ModelAndView可将包含数据对象与模板引擎进行绑定
    • SpringMVC中默认的View是JSP, 也可以配置其他模板引擎

提问:为什么不直接访问view.jsp 而是绕了一个圈子还是同样的效果
回答:因为jsp页面是写死的如何将页面和数据绑定在一起呢?先通过访问/um/view这个controller让其方法中产生数据, 之后再将这个数据通过modelandview对象绑定到页面中才可以做到

从请求传来一个用户编号把数据查询出来得到一个user用户对象 然后再view.jsp中把刚刚查询的对象进行显示[动态的]

高效解耦
后端:com/imooc/springmvc/controller/URLMappingController.java
// http://localhost/um/view?userId=1 数据动态查询产生
    @GetMapping("/view")
    public ModelAndView showView(Integer userId){
        ModelAndView mav = new ModelAndView("/view.jsp");
        User user = new User();
        if (userId == 1){
            user.setUsername("lily");
        }else if (userId == 2){
            user.setUsername("smith");
        }
    //  在当前请求中增加一个对象 数据绑定view.jsp
        mav.addObject("u", user);
        return mav;
    }
前端:src/main/webapp/view.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>I'm view page</h1>
<hr>
<h2>Username:${u.username}</h2>
</body>
</html>

ModelAndView [重定向]

[SpringMVC若跳转页面需要使用此对象进行数据绑定]
  • mav.addObject()方法设置的属性默认存放在当前请求中
  • 默认ModelAndView使用请求转发(forward)至页面
  • 重定向使用 new ModelAndView(“redirect:/index.jsp”) 新请求

页面重定向使用时机:内部的controller处理逻辑和跳转页面之间没有直接关系时可以用”redirect:/view.jsp”

ModelAndView mav = new ModelAndView("redirect:/view.jsp");
直接访问到了jsp  http://localhost/view.jsp

访问:http://localhost/um/view?userId=1
建立一个 没有斜杠的相对路径 src/main/webapp/um/view.jsp
com/imooc/springmvc/controller/URLMappingController.java
 // http://localhost/um/view?userId=1 数据动态查询产生
    @GetMapping("/view")
    public ModelAndView showView(Integer userId){
//        ModelAndView mav = new ModelAndView("redirect:/view.jsp");
        ModelAndView mav = new ModelAndView();
        mav.setViewName("view.jsp");
//        没有斜杠是代表相对路径 相对于@RequestMapping("/um")地址
//        mav.setViewName("view.jsp");

        User user = new User();
        if (userId == 1){
            user.setUsername("lily");
        }else if (userId == 2){
            user.setUsername("smith");
        }
//      在当前请求中增加一个对象 数据绑定view.jsp
        mav.addObject("u", user);
        return mav;
    }
String与ModelMap实现ModelAndView类似功能 [工作中的小技巧]
com/imooc/springmvc/controller/URLMappingController.java
//   String 与 ModelMap[为模型数据] 返回字符串
//    Controller方法返回String的情况
//    1.方法被@ResponseBody描述, 则SpringMVC直接响应Spring字符串本身
//    2.方法不存在@ResponseBody, 则SpringMVC处理String指代的视图(页面)
// 这里的String是直接代替了@ResponseBody - 产生响应文本
    public String showView1(Integer userId, ModelMap modelMap){
        String view = "/um/view.jsp";
        User user = new User();
        if (userId == 1){
            user.setUsername("lily");
        }else if (userId == 2){
            user.setUsername("smith");
        }
        modelMap.addAttribute("u", user);
        return view;
    }

SpringMVC整合Freemarker [项目:first-springmvc]

① pom.xml引入依赖
要随时导入包 tomcat → Edit configuration → Deployment → Artifacts 将右侧包put in
<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.28</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>5.1.9.RELEASE</version>
</dependency>
② 启用Freemarker模板引擎
applicationContext.xml
<bean id="ViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
   <!-- 设置响应输出,并解决中文乱码 --> 
<!-- 渲染完成后向客户端浏览器响应式 响应体中使用的字符集编码 -->
   <property name="contentType" value="text/html;charset=utf-8"></property>
   <!-- 指定Freemarker模板文件扩展名 -->
   <property name="suffix" value=".ftl"/>
</bean>
③ 本身配置Freemarker参数
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<!-- 设置模板保存的目录 tomcat无法直接解析freemarker模板引擎 所以新增/WEB-INF/ftl -->
   <property name="templateLoaderPath" value="/WEB-INF/ftl"/>
   <!-- 其他模板引擎设置 -->
   <property name="freemarkerSettings">
     <props>
         <!-- 设置Freemarker脚本与数据渲染时使用的字符集 -->
         <!-- 模板与数据绑定渲染的过程中使用的字符集编码 -->
         <prop key="defaultEncoding">UTF-8</prop>
     </props> 
   </property>
</bean>
src/main/webapp/WEB-INF/ftl/test.ftl
<h1>${u.username}</h1>
com/imooc/springmvc/controller/FreemarkerController.java
package com.imooc.springmvc.controller;

import com.imooc.springmvc.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
// http://localhost/fm/test  显示 andy
@Controller
@RequestMapping("/fm")
public class FreemarkerController {
    @GetMapping("/test")
    public ModelAndView showTest(){
//        因为之前配置所有配置扩展名是.ftl 所以下面不用写
        ModelAndView mav = new ModelAndView("/test");
        User user = new User();
        user.setUsername("andy");
        mav.addObject("u",user);
        return mav;
    }
}
=====================================================
// http://localhost/fm/test  显示 andy

RESTful开发风格

RESTful 风格(详细介绍 + 案例实现)_c# restful风格接口-CSDN博客

REST与RESTful

  • REST- 表现层状态转换, 资源在网络中以某种表现形式进行状态转移
  • RESTful是基于REST理念的一套开发风格, 是具体的开发规则

RESTful开发规范

URL中所有的都是名词 请求都有不同的含义 返回的数据是JSON或者XML格式

  • 使用URL作为用户交互入口
  • 明确的语义规范(GET, POST, PUT, DELETE)
  • 只返回数据(JSON, XML)不包含任何展现
注解 作用
@RestController 由 @Controller + @ResponseBody组成(返回 JSON 数据格式)
@PathVariable URL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到控制器处理方法的形参中
@RequestMapping 注解用于请求地址的解析,是最常用的一种注解
@GetMapping 查询请求
@PostMapping 添加请求
@PutMapping 更新请求
@DeleteMapping 删除请求
@RequestParam 将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)

RESTful命名要求

URI 说明 修改建议
GET /articles?au=lily 正确用法
GET /a/1 URI必须具有语义 GET /student/1
POST /createArticle/1 URI必须使用名词 POST /article/1
GET /articles/author/1 URI扁平化, 不超两级 GET /articles/author?id=1
DELETE/articles/1 URI名词区分单复数 GET /articles?au=lily
DELETE /article/1

开发第一个RESTful应用

404究极报错
项目生成文件out/artifacts/XXX/WEB-INF中没有导入lib包
没有将下面的web文件里的web.xml手动复制进去

pom.xml 
写完pom.xml后记得在tomcat配置中的Deployment中加入依赖包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.imooc</groupId>
    <artifactId>restful</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
</project>
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mv="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.imooc.restful"/>
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <!-- response.setContentType("text/html;charset=utf-8") -->
                        <value>text/html;charset=utf-8</value>
                        <value>application/json;charset=utf-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <mvc:default-servlet-handler/>
</beans>
web/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>characterFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
com/imooc/restful/controller/RestfulController.java
package com.imooc.restful.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/restful")
public class RestfulController {
    @GetMapping("/request")
    @ResponseBody
    public String doGetRequest(){
//  双引号中如果包括双引号 要用转义字符 \"
        return "{\"message\":\"返回查询结果\"}";
    }
}

=======================================================
http://localhost/restful/request
{"message":"返回查询结果"}

实现RESTful实验室

com/imooc/restful/controller/RestfulController.java
package com.imooc.restful.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/restful")
public class RestfulController {
    @GetMapping("/request")
    @ResponseBody
    public String doGetRequest(){
//  双引号中如果包括双引号 要用转义字符 \"
        return "{\"message\":\"返回查询结果\"}";
    }
    @PostMapping("/request")
    @ResponseBody
    public String doPostRequest(){
        return "{\"message\":\"数据新建成功\"}";
    }
    @PutMapping ("/request")
    @ResponseBody
    public String doPutRequest(){
        return "{\"message\":\"数据更新成功\"}";
    }
    @DeleteMapping("/request")
    @ResponseBody
    public String doDeleteRequest(){
        return "{\"message\":\"数据删除成功\"}";
    }
}
===========================
http://localhost/client.html
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mv="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="com.imooc.restful"/>
    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <!-- response.setContentType("text/html;charset=utf-8") -->
                        <value>text/html;charset=utf-8</value>
                        <!--只要响应产生就会使用utf-8字符集-->
                        <value>application/json;charset=utf-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    <mvc:default-servlet-handler/>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>characterFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.imooc</groupId>
    <artifactId>restful</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>aliyun</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.9</version>
        </dependency>
    </dependencies>
</project>
web/client.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery-3.3.1.min.js"></script>
    <script>
        $(function () {
            $("#btnGet").click(function () {
                $.ajax({
                    url: "/restful/request",
                    type: "get",
                    dataType: "json",
                    success: function (json) {
                        $("#message").text(json.message);
                    }
                })
            });
        })
        $(function () {
            $("#btnPost").click(function () {
                $.ajax({
                    url: "/restful/request",
                    type: "post",
                    dataType: "json",
                    success: function (json) {
                        $("#message").text(json.message);
                    }
                })
            });
        })
        $(function () {
            $("#btnPut").click(function () {
                $.ajax({
                    url: "/restful/request",
                    type: "put",
                    dataType: "json",
                    success: function (json) {
                        $("#message").text(json.message);
                    }
                })
            });
        })
        $(function () {
            $("#btnDelete").click(function () {
                $.ajax({
                    url: "/restful/request",
                    type: "delete",
                    dataType: "json",
                    success: function (json) {
                        $("#message").text(json.message);
                    }
                })
            });
        })
    </script>
</head>
<body>
    <input type="button" id="btnGet" value="发送Get请求">
    <input type="button" id="btnPost" value="发送Post请求">
    <input type="button" id="btnPut" value="发送Put请求">
    <input type="button" id="btnDelete" value="发送Delete请求">
    <h1 id="message"></h1>
</body>
</html>

RestController注解与路径变量

在post请求中通过client.html利用Ajax动态注入了一个rid到RestfulController.java
代替了所有的@ResponseBody 默认向请求台进行输出

@Controller: 用于标识一个类是Spring MVC中的控制器,类似于标准的@Controller 注解。它告诉Spring框架该类是一个控制器,可以处理HTTP请求。

@ResponseBody: 用于将方法的返回值直接作为HTTP响应的主体(Body)内容。这意味着不会进行视图解析,而是直接将返回的对象(通常是JSON或XML)写入HTTP响应

因此,@RestController 的作用是将一个控制器类标记为RESTful风格的控制器,其中的每个方法都被视为返回数据,而不是视图。这样就不需要在每个方法上都添加 @ResponseBody 注解,因为该注解已经包含在 @RestController 中。
client.html
 $(function(){
            $("#btnPost").click(function () {
                $.ajax({
                    url : "/restful/request/100",
                    type : "post" ,
                    dataType : "json" ,
                    success : function(json){
                   $("#message").text(json.message+":"+json.id);
                    }
                })
            });
        })
com/imooc/restful/controller/RestfulController.java
    
package com.imooc.restful.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/restful")
//@CrossOrigin(origins = {"http://localhost:8080","http://www.imooc.com"})
//@CrossOrigin(origins = "*",maxAge = 3600)
public class RestfulController {
    @GetMapping("/request")
    //@ResponseBody
    public String doGetRequest() {
        return "{\"message\":\"返回查询结果\"}";
    }

    // POST /article/1
    // POST /restful/request/100
    @PostMapping("/request/{rid}")
    //@ResponseBody
    public String doPostRequest(@PathVariable("rid") Integer requestId) {
        return "{\"message\":\"数据新建成功\",\"id\":" + requestId + "}";
    }

    @PutMapping("/request")
    //@ResponseBody
    public String doPutRequest() {
        return "{\"message\":\"数据更新成功\"}";
    }

    @DeleteMapping("/request")
    //@ResponseBody
    public String doDeleteRequest() {
        return "{\"message\":\"数据删除成功\"}";
    }
}

简单请求与非简单请求

  • 简单请求是指标准结构的HTTP请求, 对应GET/POST请求
  • 非简单请求是复杂要求的HTTP请求, 指PUT/DELETE、扩展标准请求
  • 两者最大区别是非简单请求发送前需要发送预检请求 [看看能不能进行处理 可以才实际处理 预先处理不符合的数据挡在外面]
web.xml
<!--    对put和delete请求进行支持-->
    <filter>
        <filter-name>formContentFilter</filter-name>
        <filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>formContentFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
web/client.html
$(function(){
            $("#btnPost").click(function () {
                $.ajax({
                    url : "/restful/request/100",
                    type : "post" ,
                    data : "name=lily&age=23",
                    dataType : "json" ,
                    success : function(json){
                        $("#message").text(json.message+":"+json.id);
                    }
                })
            });
        })

        $(function(){
            $("#btnPut").click(function () {
                $.ajax({
                    url : "/restful/request",
                    type : "put" ,
                    data : "name=lily&age=23",
                    dataType : "json" ,
                    success : function(json){
                        $("#message").text(json.message);
                    }
                })
            });
        })
com/imooc/restful/controller/RestfulController.java
 // POST /article/1
    // POST /restful/request/100
    @PostMapping("/request/{rid}")
    //@ResponseBody
    public String doPostRequest(@PathVariable("rid") Integer requestId, Person person){
        System.out.println(person.getName() + ":" + person.getAge());
        return "{\"message\":\"数据新建成功\",\"id\":" + requestId + "}";
    }

    @PutMapping("/request")
    //@ResponseBody
    public String doPutRequest(Person person){
        System.out.println(person.getName() + ":" + person.getAge());
        return "{\"message\":\"数据更新成功\"}";
    }
====================
lily:23

创建一个Person用来插入数据 并打印在控制台

com/imooc/restful/entity/Person.java
public class Person {
    private String name;
    private Integer age;
}Getter + Setter

JSON序列化

pom.xml
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.9</version>
        </dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.9</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.9</version>
        </dependency>
一定要使用2.9以后的版本 之前的版本有严重的安全风险
http://localhost/restful/person?id=1
@GetMapping("/person")
//不返回String 返回JSON序列化的对象
    public Person findByPersonId(Integer id){
        Person p = new Person();
        if (id==1){
            p.setName("lily");
            p.setAge(23);
        } else if (id==2) {
            p.setName("smith");
            p.setAge(22);
        }
        return p;
    }
============================================
{
    "name": "lily",
    "age": 23
}



http://localhost/restful/persons
@GetMapping("/persons")
    public List<Person> findPersons(){
        List list = new ArrayList();
        Person p1 = new Person();
        p1.setName("lily");
        p1.setAge(23);
        list.add(p1);

        Person p2 = new Person();
        p2.setName("smith");
        p2.setAge(22);
        list.add(p2);
        
        return list;
    }
=============================================
[
    {
        "name": "lily",
        "age": 23
    },
    {
        "name": "smith",
        "age": 22
    }
]

服务器返回json数组

client.html
$(function(){
            $("#btnPersons").click(function () {
                $.ajax({
                    url : "/restful/persons",
                    type : "get" ,
                    dataType : "json" ,
                    success : function(json){
                        console.info(json);
                        for (var i=0; i<json.length; i++){
                            var p = json[i];
                            $("#divPersons").append("<h2>" + p.name + "-" + p.age + "</h2>")
                        }
                    }
                })
            });
        })

    </script>
</head>
<body>
    <input type="button" id="btnGet" value="发送Get请求">
    <input type="button" id="btnPost" value="发送Post请求">
    <input type="button" id="btnPut" value="发送Put请求">
    <input type="button" id="btnDelete" value="发送Delete请求">
    <h1 id="message"></h1>
    <hr/>
    <!-- 点击btnPersons按钮时发送Ajax请求[上面有操作代码] 将所有人员信息追加到div中-->
    <input type="button" id="btnPersons" value="查询所有人员">
    <div id="divPersons"></div>
</body>
com/imooc/restful/controller/RestfulController.java
@GetMapping("/persons")
    public List<Person> findPersons(){
        List list = new ArrayList();
        Person p1 = new Person();
        p1.setName("lily");
        p1.setAge(23);
        list.add(p1);

        Person p2 = new Person();
        p2.setName("smith");
        p2.setAge(22);
        list.add(p2);

        return list;
    }

===============================================
http://localhost/client.html
点击查询所有人员
=> 
lily-23
smith-22

增加时间!

Person.java
public class Person {
    private String name;
    private Integer age;
// 记得用日期的格式化输出
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date birthday;
}Getter + Setter
client.html
$(function(){
            $("#btnPersons").click(function () {
                $.ajax({
                    url : "/restful/persons",
                    type : "get" ,
                    dataType : "json" ,
                    success : function(json){
                        console.info(json);
                        for (var i=0; i<json.length; i++){
                            var p = json[i];
                            $("#divPersons").append("<h2>" + p.name + "-" + p.age + "-" + p.birthday+ "</h2>")
                        }
                    }
                })
            });
        })

===================================
lily-23-2024-01-17 09:09:28
smith-22-2024-01-17 09:09:28

浏览器的同源策略

  • 同源策略阻止从一个域加载的脚本去获取另一个域上的资源
  • 只要协议域名端口有任何一个不同,都被当做是不同的域
  • 浏览器Console看到Access-Control-Allow-Origin就代表了跨域了

HTML中允许跨域的标签

  • < img > - 显示远程图片
  • < script > - 加载远程JS
  • < link > - 加载远程CSS

CORS跨域资源访问

  • CORS是一种机制, 使用额外的HTTP头通知浏览器可以访问其他域
  • URL响应头包含 Access-Control-* 指明请求允许跨域

Spring MVC解决跨域访问

  • @CrossOrigin - Controller跨域注解
  • < mvc:cors > - Spring MVC全局跨域配置
RestfulController.java [代码第四行]
@RestController
@RequestMapping("/restful")
//@CrossOrigin(origins = {"http://localhost:8080","http://www.imooc.com"})
//@CrossOrigin(origins = "*",maxAge = 3600) 所有端口都会访问发送请求
//maxAge = 3600 一小时时间后发送预检请求  之内的就发送实际请求 【非简单请求】
public class RestfulController {
    @GetMapping("/request")
    //@ResponseBody
    public String doGetRequest(){
        return "{\"message\":\"返回查询结果\"}";
    }

    // POST /article/1
    // POST /restful/request/100
    @PostMapping("/request/{rid}")
    //@ResponseBody
    public String doPostRequest(@PathVariable("rid") Integer requestId, Person person){
        System.out.println(person.getName() + ":" + person.getAge());
        return "{\"message\":\"数据新建成功\",\"id\":" + requestId + "}";
    }

    @PutMapping("/request")
    //@ResponseBody
    public String doPutRequest(Person person){
        System.out.println(person.getName() + ":" + person.getAge());
        return "{\"message\":\"数据更新成功\"}";
    }

    @DeleteMapping("/request")
    //@ResponseBody
    public String doDeleteRequest(){
        return "{\"message\":\"数据删除成功\"}";
    }

    @GetMapping("/person")
    public Person findByPersonId(Integer id){
        Person p = new Person();
        if (id==1){
            p.setName("lily");
            p.setAge(23);
        } else if (id==2) {
            p.setName("smith");
            p.setAge(22);
        }
        return p;
    }

    @GetMapping("/persons")
    public List<Person> findPersons(){
        List list = new ArrayList();
        Person p1 = new Person();
        p1.setName("lily");
        p1.setAge(23);
        p1.setBirthday(new Date());
        list.add(p1);

        Person p2 = new Person();
        p2.setName("smith");
        p2.setAge(22);
        p2.setBirthday(new Date());
        list.add(p2);
        return list;
    }
}

CORS全局配置

<mvc:cors>
    <mvc:mapping path="/restful/**"
        allowed-origins="http://localhost:8080,http://www.imooc.com"
        max-age="3600"/>
</mvc:cors>

SpringMVC拦截器 [高级组件]

拦截器-Interceptor
  • 拦截器(Interceptor)用于对URL请求进行前置/后置过滤
  • Interceptor与Filter用途相似, 但实现方式不同
  • Interceptor底层基于Spring AOP面向切面编程实现[类似于环绕通知]
拦截器开发流程
  • Maven依赖servlet-api
  • 实现HandlerInterceptor接口
  • applicationContext配置过滤地址
HandlerInterceptor接口
  • preHandle - 前置执行处理
  • postHandle - 目标资源已被Spring MVC框架处理 没产生响应文本
  • afterCompletion - 相应文本已经产生
pom.xml
     <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
<!-- 只有在开发编译才会引用 打包最终使用的时候会排除在外 因为 servlet-api与tomcat-api冲突  -->
            <scope>provided</scope>
        </dependency>
    </dependencies>
com/imooc/restful/interceptor/MyInterceptor.java
package com.imooc.restful.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
//    Code -> Implement Methods

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(request.getRequestURI()+"准备执行");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(request.getRequestURI()+"目标处理成功");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(request.getRequestURI()+"响应内容已产生");
    }
//  随后去applicationContext.xml配置
}

=============================================================
/准备执行
/目标处理成功
/响应内容已产生
/准备执行
/目标处理成功
/响应内容已产生
/准备执行
/目标处理成功
/响应内容已产生
    
http://localhost/restful/persons
/restful/persons准备执行
/restful/persons目标处理成功
/restful/persons响应内容已产生
    
http://localhost/client.html
/client.html准备执行
/client.html目标处理成功
/client.html响应内容已产生
applicationContext.xml
<mvc:interceptors>
        <mvc:interceptor>
<!--  哪些[所有]地址进行拦截 下面加个bean是哪个类进行处理[送到这个类中处理]-->
            <mvc:mapping path="/**"/>
            <bean class="com.imooc.restful.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

拦截器使用细则

applicationContext.xml
<mvc:interceptors>
        <mvc:interceptor>
<!--            哪些[所有]地址进行拦截 下面加个bean是哪个类进行处理[送到这个java中处理]-->
       <!-- <mvc:mapping path="/**"/> -->
            <mvc:mapping path="/restful/**"/>
            <mvc:mapping path="/webapi/**"/>
<!--            以下是不需要拦截 排除在外的-->
            <mvc:exclude-mapping path="/**.ico"/>
            <mvc:exclude-mapping path="/**.jpg"/>
            <mvc:exclude-mapping path="/**.gif"/>
            <mvc:exclude-mapping path="/**.js"/>
            <mvc:exclude-mapping path="/**.css"/>
<!--   创建一个resources  规范静态文件目录 一次性都排除在外-->
            <mvc:exclude-mapping path="/rescources/**"/>

            <bean class="com.imooc.restful.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(request.getRequestURI()+"准备执行");
        return true;
    }
通过preHandle的返回值可以做很多事情
对某一个url进行前置检查 对请求放行 如果不符合要求 直接在拦截器中返回相应
return true; 请求依次向后发送
return false; 请求被阻挡

开发”用户流量”拦截器

pom.xml [引入新的依赖后一定要去Tomcat导入新的依赖包]
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%thread] %d %level %logger{10} - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="accessHistoryLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>d:/logs/history.%d.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>[%thread] %d %level %logger{10} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="debug">
        <appender-ref ref="console"/>
    </root>
    <logger name="com.imooc.restful.interceptor.AccessHistoryInterceptor"
            level="INFO" additivity="false">
        <appender-ref ref="accessHistoryLog"/>
    </logger>
</configuration>
com/imooc/restful/interceptor/AccessHistoryInterceptor.java
package com.imooc.restful.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AccessHistoryInterceptor implements HandlerInterceptor {
    private Logger logger = LoggerFactory.getLogger(AccessHistoryInterceptor.class);
//    前置处理

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        StringBuilder log = new StringBuilder();
        log.append(request.getRemoteAddr());
        log.append("|");
        log.append(request.getRequestURL());
        log.append("|");
        log.append(request.getHeader("user-agent"));
        logger.info(log.toString());
        return true;
    }
}
com/imooc/restful/interceptor/MyInterceptor.java
package com.imooc.restful.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {
//    Code -> Implement Methods

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println(request.getRequestURL() + "-准备执行");
//        response.getWriter().print("[]");
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(request.getRequestURL() + "-目标处理成功");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(request.getRequestURL() + "-响应内容已产生");
    }
}

==============================================================
http://localhost/client.html
[http-nio-80-exec-1] 2024-01-18 10:43:54,739 DEBUG o.s.w.s.DispatcherServlet - GET "/login.html", parameters={}
[http-nio-80-exec-1] 2024-01-18 10:43:54,750 DEBUG o.s.w.s.h.SimpleUrlHandlerMapping - Mapped to org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler@5c0e8bb9
[http-nio-80-exec-1] 2024-01-18 10:43:54,758 DEBUG o.s.w.s.DispatcherServlet - Completed 404 NOT_FOUND
[http-nio-80-exec-2] 2024-01-18 10:44:00,495 DEBUG o.s.w.s.DispatcherServlet - GET "/restful/request", parameters={}
[http-nio-80-exec-2] 2024-01-18 10:44:00,502 DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.lang.String com.imooc.restful.controller.RestfulController.doGetRequest()
http://localhost/restful/request-准备执行
[http-nio-80-exec-2] 2024-01-18 10:44:00,539 DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'application/json;charset=utf-8', given [application/json, text/javascript, */*;q=0.01] and supported [text/html;charset=utf-8, application/json;charset=utf-8, text/plain, */*, application/json, application/*+json]
[http-nio-80-exec-2] 2024-01-18 10:44:00,539 DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing ["{"message":"返回查询结果"}"]
http://localhost/restful/request-目标处理成功
http://localhost/restful/request-响应内容已产生
[http-nio-80-exec-2] 2024-01-18 10:44:00,580 DEBUG o.s.w.s.DispatcherServlet - Completed 200 OK
[http-nio-80-exec-5] 2024-01-18 10:44:08,194 DEBUG o.s.w.s.DispatcherServlet - GET "/restful/persons", parameters={}
[http-nio-80-exec-5] 2024-01-18 10:44:08,195 DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped to public java.util.List<com.imooc.restful.entity.Person> com.imooc.restful.controller.RestfulController.findPersons()
http://localhost/restful/persons-准备执行
RestfulController.findPersons() - return list
[http-nio-80-exec-5] 2024-01-18 10:44:08,211 DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Using 'application/json', given [application/json, text/javascript, */*;q=0.01] and supported [application/json, application/*+json]
[http-nio-80-exec-5] 2024-01-18 10:44:08,211 DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Writing [[com.imooc.restful.entity.Person@619c4d80, com.imooc.restful.entity.Person@71459fb]]
http://localhost/restful/persons-目标处理成功
http://localhost/restful/persons-响应内容已产生
===================================================================
D:\logs\history.2024-01-18
[http-nio-80-exec-1] 2024-01-18 10:43:54,755 INFO c.i.r.i.AccessHistoryInterceptor - 0:0:0:0:0:0:0:1|http://localhost/login.html|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
[http-nio-80-exec-2] 2024-01-18 10:44:00,502 INFO c.i.r.i.AccessHistoryInterceptor - 0:0:0:0:0:0:0:1|http://localhost/restful/request|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
[http-nio-80-exec-5] 2024-01-18 10:44:08,195 INFO c.i.r.i.AccessHistoryInterceptor - 0:0:0:0:0:0:0:1|http://localhost/restful/persons|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
[http-nio-80-exec-3] 2024-01-18 10:44:10,137 INFO c.i.r.i.AccessHistoryInterceptor - 0:0:0:0:0:0:0:1|http://localhost/restful/request/100|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
[http-nio-80-exec-4] 2024-01-18 10:44:10,935 INFO c.i.r.i.AccessHistoryInterceptor - 0:0:0:0:0:0:0:1|http://localhost/restful/request|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
[http-nio-80-exec-6] 2024-01-18 10:44:11,481 INFO c.i.r.i.AccessHistoryInterceptor - 0:0:0:0:0:0:0:1|http://localhost/restful/request|Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
applicationContext.xml
<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/resources/**"/>
            <bean class="com.imooc.restful.interceptor.AccessHistoryInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

Spring MVC处理流程